# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: inkyu

from coin.base.datetime_util import to_datetime
from coin.strategy.mm.base.dump.pass_unhedge import FeatureRow
from coin.strategy.mm.calculator import MovingSum
from coin.exchange.base.tick import FixedTickPrice


class Pricer(object):
  def __init__(self,
               basis_ma_window,
               tick,
               price_multiplier_1,
               book_askt_func_1,
               book_bidt_func_1,
               book_askt_func_2,
               book_bidt_func_2):
    self._basis_ma_window = basis_ma_window
    self._tick = FixedTickPrice(tick) if isinstance(tick, float) else tick
    self._price_multiplier_1 = price_multiplier_1
    self._book_askt_funcs = {1: book_askt_func_1, 2: book_askt_func_2}
    self._book_bidt_funcs = {1: book_bidt_func_1, 2: book_bidt_func_2}

    self._ready = False
    self._ask_basis_ma = MovingSum(self._basis_ma_window)
    self._bid_basis_ma = MovingSum(self._basis_ma_window)
    self._last = None

  @property
  def tick(self):
    return self._tick

  @property
  def ready(self):
    return self._ready

  @property
  def last_value(self):
    return self._last

  def reset(self):
    self._ready = False
    self._last = None

  def update(self, book_1, book_2):
    book_ts = max(book_1.timestamp, book_2.timestamp)
    min_ts = min(book_1.timestamp, book_2.timestamp)

    if book_ts - min_ts > (2 * 10**9):
      self.reset()
      return

    if not (book_1.has_ask() and book_1.has_bid() and book_2.has_ask() and book_2.has_bid()):
      self.reset()
      return

    # Exchange 1
    ask0_1 = book_1.ask0().price
    bid0_1 = book_1.bid0().price
    askt_1 = self._book_askt_funcs[1](book_1)
    bidt_1 = self._book_bidt_funcs[1](book_1)

    # Exchange 2
    ask0_2 = book_2.ask0().price
    bid0_2 = book_2.bid0().price
    askt_2 = self._book_askt_funcs[2](book_2)
    bidt_2 = self._book_bidt_funcs[2](book_2)

    askt_diff = askt_1 * self._price_multiplier_1 - askt_2
    bidt_diff = bidt_1 * self._price_multiplier_1 - bidt_2

    self._ask_basis_ma.update(book_ts, askt_diff)
    self._bid_basis_ma.update(book_ts, bidt_diff)

    if len(self._ask_basis_ma) < 100 or len(self._bid_basis_ma) < 100:
      self.reset()
      return

    # Set ready
    self._last = FeatureRow(book_ts,
                            str(to_datetime(book_ts)),
                            ask0_1,
                            askt_1,
                            bid0_1,
                            bidt_1,
                            ask0_2,
                            askt_2,
                            bid0_2,
                            askt_2,
                            self._ask_basis_ma.average,
                            self._bid_basis_ma.average)
    self._ready = True

  def calculate_price(self, sell_edge, buy_edge, passive=True):
    assert self.ready
    assert -1 < sell_edge < 1
    assert -1 < buy_edge < 1

    askt_1_proj_2 = (self._last.askt_1 * self._price_multiplier_1
                     - float(self._ask_basis_ma.average))
    bidt_1_proj_2 = (self._last.bidt_1 * self._price_multiplier_1
                     - float(self._bid_basis_ma.average))

    if passive:
      ask0_minus_one_2 = self._tick.get_prev_price(self._last.ask0_2)
      bid0_plus_one_2 = self._tick.get_next_price(self._last.bid0_2)
      askp_2 = max(self._tick.ceil(askt_1_proj_2 * (1. + sell_edge)),
                   ask0_minus_one_2,
                   bid0_plus_one_2)
      bidp_2 = min(self._tick.floor(bidt_1_proj_2 * (1. - buy_edge)),
                   ask0_minus_one_2,
                   bid0_plus_one_2)
    else:
      askp_2 = self._tick.ceil(askt_1_proj_2 * (1. + sell_edge))
      bidp_2 = self._tick.floor(bidt_1_proj_2 * (1. - buy_edge))

    return askp_2, bidp_2
